package operativa.action;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import operativa.action.utils.CSVGenerator;
import operativa.action.utils.LocationProbability;
import operativa.action.utils.LocationRandomFishBins;
import operativa.action.utils.SimulationRow;
import operativa.action.utils.UbicacionRecorrido;
import operativa.bean.entity.Location;
import operativa.bean.entity.SimulationConfig;
import operativa.model.dao.LocationDAO;
import operativa.utils.Constantes.LocationType;
import algorithm.OptimalTransport;
import algorithm.Simulation;
import algorithm.TransportMatrix;
import algorithm.VogelAlgorithm;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import com.opensymphony.xwork2.config.entities.Parameterizable;

public class SimulationAction extends ActionSupport implements
		ModelDriven<SimulationConfig>, Preparable, Parameterizable {

	private static final long serialVersionUID = 1L;
	private List<Location> fishShops;
	private List<Location> harbors;
	private LocationDAO locationDAO = new LocationDAO();
	private List<SimulationRow> simulationResult = new ArrayList<SimulationRow>();

	private Map<String, LocationProbability> fishShopMap = new HashMap<String, LocationProbability>();
	private Map<String, LocationRandomFishBins> harborMap = new HashMap<String, LocationRandomFishBins>();
	
	ArrayList<LocationProbability> probList = new ArrayList<LocationProbability>() ;
	ArrayList<LocationRandomFishBins> ofertList = new ArrayList<LocationRandomFishBins>() ;

	private List<Location> fishShopsList;
	private List<Location> harborsList;

	private String selFishShops;
	private Map<String, Object> params;
	private TransportMatrix matrix;
	private String error = "";

	public TransportMatrix getMatrix() {
		return matrix;
	}

	public void setMatrix(TransportMatrix matrix) {
		this.matrix = matrix;
	}

	private SimulationConfig simulationConfig = new SimulationConfig();

	@Override
	public void prepare() throws Exception {

		fishShops = locationDAO.getByTipoUbicacion(LocationType.FISHSHOP
				.toString());
		for (Location f : fishShops) {
			fishShopMap.put(f.getId().toString(), new LocationProbability(f));
		}

		harbors = locationDAO
				.getByTipoUbicacion(LocationType.HARBOR.toString());
		for (Location f : harbors) {
			harborMap.put(f.getId().toString(), new LocationRandomFishBins(f));
		}

	}

	public String init() {
		return SUCCESS;
	}

	public List<UbicacionRecorrido> getTravelList(List<Location> ubicaciones) {
		List<UbicacionRecorrido> result = new ArrayList<UbicacionRecorrido>();

		for (Location u : ubicaciones) {
			result.add(new UbicacionRecorrido(u, "false"));
		}

		return result;
	}

	public void setSelected() {
		fishShopsList = new ArrayList<Location>();

		for (String s : fishShopMap.keySet()) {
			if (fishShopMap.get(s).getChoosen().equals("true"))
				fishShopsList.add(fishShopMap.get(s).getLocation());
		}

		harborsList = new ArrayList<Location>();

		for (String s : harborMap.keySet()) {
			if (harborMap.get(s).getChoosen().equals("true"))
				harborsList.add(harborMap.get(s).getLocation());
		}
	}

	public String runSimulation() {
		simulationResult = new ArrayList<SimulationRow>();
		this.setSelected();
		if (fishShopsList.size() == 0 || harborsList.size() == 0) {
			System.out.println("ERROR NON SELECTION");
			error = getText("error.noselection");
			return "error";
		}
		
		ofertList = new ArrayList<LocationRandomFishBins>();
		for (Location harbor : harborsList) {
			LocationRandomFishBins ofert = new LocationRandomFishBins(harbor);
			ofert.setMinFishBins(harborMap.get(
					harbor.getId().toString()).getMinFishBins());
			ofert.setMaxFishBins(harborMap.get(
					harbor.getId().toString()).getMaxFishBins());
			ofertList.add(ofert);
		}
		
		if (!this.verifyOfertList(ofertList)) {
			System.out.println("ERROR OFERT BAD");
			error = getText("error.ofert");
			ofertList = new ArrayList<LocationRandomFishBins>();
			this.clearSelection();
			
			return "error";
		}

		probList = new ArrayList<LocationProbability>();
		for (Location fishShop : fishShopsList) {
			LocationProbability prob = new LocationProbability(fishShop);
			prob.setProbability(this.normalizeProbability(fishShopMap.get(
					fishShop.getId().toString()).getProbability()));
			probList.add(prob);
		}

		if (!this.verifyProbList(probList)) {
			System.out.println("ERROR PROB BAD");
			error = getText("error.probability");
			probList = new ArrayList<LocationProbability>();
			this.clearSelection();
			
			return "error";
		}

		if (!this.verifyMonths(simulationConfig.getMonths())) {
			System.out.println("ERROR MONTHS BAD");
			error = getText("error.months");
			this.clearSelection();
			return "error";
		}

		error = "";

		System.out.println(simulationConfig.getMonths());

		for (int i = 0; i < simulationConfig.getMonths(); i++) {
			Simulation simulation = new Simulation(simulationConfig,
					(ArrayList<LocationProbability>) probList, ofertList);
			// simulation.setAverage();
			simulation.setAccum();
			int demand = simulation.getTotalFishBinsFromSelectedHarbors();
			System.out.println("DEMANDA: " + demand);
			simulation.assignDemand(demand);

			// for (Iterator iterator = fishShopsList.iterator();
			// iterator.hasNext();) {
			// Location location = (Location) iterator.next();
			// this.locationDAO.makePersistent(location);
			// }

			// locationDAO.commit();

			try {
				this.calculateModel();

				//matrix.print();

				System.out.println("COSTO TOTAL: " + matrix.getTotalCost());

				SimulationRow row = new SimulationRow(matrix.getTotalCost(),
						i + 1);
				for (Location fishshop : fishShopsList) {
					row.addDemand(new Integer(fishshop.getFishbins().intValue()));
				}
				simulationResult.add(row);

			} catch (Exception ex) {
				i--;
			}
			;
		}

		// locationDAO.commit();
		try {
			CSVGenerator.generateCsvFile("c:\\SimulationResult.csv",
					simulationResult, fishShopsList);
		} catch (Exception ex) {
		}
		;

		this.clearSelection();

		return "SUCCESS";
	}

	private boolean verifyOfertList(ArrayList<LocationRandomFishBins> ofertList) {
		for (LocationRandomFishBins ofert : ofertList) {
			if (!ofert.isValid())
				return false;
		}
		return true;
	}

	private boolean verifyMonths(Integer months) {
		return ((months <= 300) && (months > 0));
	}

	private void clearSelection() {
		for (String s : fishShopMap.keySet()) {
			fishShopMap.get(s).setChoosen("false");
			fishShopMap.get(s).setProbabilityAccum(new Float(0f));
		}

		harborsList = new ArrayList<Location>();

		for (String s : harborMap.keySet()) {
			harborMap.get(s).setChoosen("false");
			harborMap.get(s).setMinFishBins(new Integer(0));
			harborMap.get(s).setMaxFishBins(new Integer(0));
		}
	}

	private boolean verifyProbList(ArrayList<LocationProbability> probList) {
		return ((this.totalProb(probList).floatValue() == 1));
	}

	private Float totalProb(ArrayList<LocationProbability> probList) {
		float sum = 0;
		for (LocationProbability locationProbability : probList) {
			sum += locationProbability.getProbability().floatValue();
		}

		return sum;
	}

	private Float normalizeProbability(Float prob) {
		float probNorm = prob.floatValue();
		probNorm = probNorm / 100;
		System.out.println("Prob Norm: " + probNorm);

		return new Float(probNorm);
	}

	public Float getAverageTotalCost() {
		float avg = 0;
		for (SimulationRow row : simulationResult) {
			avg += row.getTotalCost();
		}

		if (simulationResult.size() != 0) {
			float avgFinal = avg / simulationResult.size();

			return avgFinal;
		} else
			return avg;
	}

	public String calculateModel() {

		VogelAlgorithm vogel = new VogelAlgorithm();

		// HttpServletRequest request = (HttpServletRequest)
		// ActionContext.getContext().get(ServletActionContext.HTTP_REQUEST);
		// //Obtengo fabricas y destinos del request
		// System.out.println("HARBOR PARAMS: " +
		// request.getParameter("selHarbors"));
		// System.out.println("FISHSHOP PARAMS: " +
		// request.getParameter("selFishShops"));
		// String[] harborIds = request.getParameter("selHarbors").split(",");
		// String[] fishShopIds =
		// request.getParameter("selFishShops").split(",");
		// for (int i = 0; i < harborIds.length; i++) {
		// harborsList.add(locationDAO.findById(Integer.parseInt(harborIds[i]),
		// false));
		// }
		// for (int i = 0; i < fishShopIds.length; i++) {
		// fishShopsList.add(locationDAO.findById(Integer.parseInt(fishShopIds[i]),
		// false));
		// }

		// long startTime = System.currentTimeMillis();

		// Ejecuto el algoritmo con las fabricas y puntos de destino
		matrix = vogel.resolve(harborsList, fishShopsList);

		/**
		 * TODO Aca se deberia acoplar el algoritmo para sacar la mejor solucion
		 */

		OptimalTransport oT = new OptimalTransport();

		matrix = oT.getOptimalSolution(matrix);

		return SUCCESS;
	}

	public List<Location> getFishShops() {
		return fishShops;
	}

	public void setFishShops(List<Location> destinos) {
		this.fishShops = destinos;
	}

	public List<Location> getFishShopsList() {
		return fishShopsList;
	}

	public void setFishShopsList(List<Location> fishShopsList) {
		this.fishShopsList = fishShopsList;
	}

	public List<Location> getHarborsList() {
		return harborsList;
	}

	public void setHarborsList(List<Location> harborsList) {
		this.harborsList = harborsList;
	}

	public Map<String, LocationRandomFishBins> getHarborMap() {
		return harborMap;
	}

	public void setHarborMap(Map<String, LocationRandomFishBins> harborMap) {
		this.harborMap = harborMap;
	}

	public List<Location> getHarbors() {
		return harbors;
	}

	public void setHarbors(List<Location> harbors) {
		this.harbors = harbors;
	}

	public Map<String, LocationProbability> getFishShopMap() {
		return fishShopMap;
	}

	public void setFishShopMap(Map<String, LocationProbability> destMap) {
		this.fishShopMap = destMap;
	}

	public String getFishShopsListString() {
		String result = "";
		for (Location destino : fishShopsList) {
			result += destino.getId().toString() + ",";
		}
		return result.substring(0, result.length() - 1);
	}

	public String getSelFishShops() {
		return selFishShops;
	}

	public void setSelFishShops(String selFishShops) {
		this.selFishShops = selFishShops;
	}

	@Override
	public void addParam(String arg0, Object arg1) {
		this.params.put(arg0, arg1);

	}

	@Override
	public Map getParams() {
		return this.params;
	}

	@Override
	public void setParams(Map<String, Object> params) {
		this.params = params;
	}

	@Override
	public SimulationConfig getModel() {
		return simulationConfig;
	}

	public List<SimulationRow> getSimulationResult() {
		return simulationResult;
	}

	public void setSimulationResult(List<SimulationRow> simulationResult) {
		this.simulationResult = simulationResult;
	}

	public String getError() {
		return error;
	}

	public void setError(String error) {
		this.error = error;
	}

	public ArrayList<LocationProbability> getProbList() {
		return probList;
	}

	public void setProbList(ArrayList<LocationProbability> probList) {
		this.probList = probList;
	}

	
}
